/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package base;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import tal.drivechain.chain.Chain;
import tal.drivechain.chain.ChainNode;
import tal.drivechain.chain.ChainNode.MSG;
import tal.drivechain.chain.script.ScriptCommand;
import tal.drivechain.chain.shell.DriveShell;
import tal.drivechain.parsing.ParsingAid;
import tal.drivechain.system.Globals;

/**
 *
 * @author eisental
 */
public class TelnetController extends ConsoleController {
    private int port;
    public final static int DEFAULT_PORT = 2345;
    private DriveShell parser;

    public DriveShell getScriptInterpreter() {
        return parser;
    }

    @Override
    public void init(boolean timing, OutputStream out) {
        super.init(timing, out);
        if (properties.containsKey("port")) {
            String sPort = "";
            try {
                sPort = properties.get("port");
                int portNumber = Integer.parseInt(sPort);
                if (portNumber<0 || portNumber>65535) {
                    System.out.println("Port number out of range: " + port + " , using default (27)");
                    port = TelnetController.DEFAULT_PORT;
                } else port = portNumber;
            } catch (NumberFormatException ne) {
                System.out.println("Invalid port number: " + sPort + ", using default (" + DEFAULT_PORT + ")");
            }
        } else port = DEFAULT_PORT;

    }

    @Override
    public void takeOver() {
        this.runLogger();
        if (properties.containsKey("openRoot") && properties.get("openRoot").equals("true"))
            Globals.getRootChain().start();

        try {
            new TelnetServer(port, this);
        } catch (IOException ie) {
            System.out.println("Error while running telnet server: " + ie);
            ie.printStackTrace();
        }
        this.stopLogger();
    }

    @Override
    public void logReceived(LogLine currentLog) {
        if (parser!=null)
            parser.getPrintStream().print("| ");
        super.logReceived(currentLog);
    }

    @Override
    public boolean chainStopping(Chain chain) {
        return true;
    }

}
class TelnetServer extends ServerSocket {
    TelnetController listener;

    public TelnetServer(int port, TelnetController listener) throws IOException {
        super(port);
        this.listener = listener;
            System.out.println("--- Waiting for telnet client on port " + port + "...");
        while (true) {
            TelnetShell shell = new TelnetShell(this.accept(), listener);
            Thread shellThread = new Thread(shell);
            shellThread.start();
        }
    }
}

class TelnetShell implements Runnable {
    Socket client;
    TelnetController listener;

    public TelnetShell(Socket client, TelnetController listener) { this.client = client; this.listener = listener; }

    public void run() {
        PrintStream out = null;
        BufferedReader in = null
                ;
        try {
            out = new PrintStream(client.getOutputStream(), true);
            listener.setOutputStream(client.getOutputStream());
            in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        } catch (IOException ex) {
            System.out.println("Error while connecting to client: " + ex.getMessage());
        }

        String inputLine;
        System.out.println("Connection established with: " + client.getInetAddress().toString() + " port " + client.getPort());
//        out.println("\ntype @clear; and press enter to clear the current command buffer.");
//        out.println("type @repeat; to repeat the last command.");
        out.println();
        String command = "", lastCommand = null;
        DriveShell shell = new DriveShell(out);
        shell.setActiveChain(Globals.getRootChain());
        out.print("| ");
        try {
            while ((inputLine = "" + in.readLine()) != null) {
                if (inputLine.equals("@clear;")) {
                    command = "";
                    out.print("|\n| ");
                } else if (inputLine.equals("@repeat;")) {
                    if (lastCommand==null)
                        out.println("Nothing to repeat...");
                    else {
                        runShell(command, shell);
                        command = "";
                        out.print("|\n| ");
                    }
                } else {
                    command += inputLine.trim();
                    if (command.length() != 0 && command.charAt(command.length() - 1) == ';' && !ParsingAid.isNested(command, command.length() - 1, "{(['", "})]'")) {
                        runShell(command, shell);
                        lastCommand = command;
                        command = "";
                        out.print("|\n| ");
                    } else {
                        command += " ";
                        out.print(": ");
                    }
                }
            }
            client.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    private void runShell(String command, DriveShell shell) {
        try {
            ScriptCommand[] commands = DriveShell.parseCommand(command);
            for (ScriptCommand c : commands) shell.runCommand(c);
        } catch (IllegalArgumentException e) {
            listener.generalMessage(e.getMessage(), MSG.ERROR, "shell");
        }
    }
}